package io.renren.common.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
 * jackson 工具类
 *
 * @author alex
 * @since 2019-09-15
 */
public class JacksonUtils {

    private JacksonUtils() {
    }

    private final static ObjectMapper objectMapper;

    static {
        objectMapper = initObjectMapper(new ObjectMapper());
    }

    public static ObjectMapper getInstance() {
        return objectMapper;
    }

    /**
     * 初始化 ObjectMapper
     *
     * @param objectMapper
     * @return
     */
    public static ObjectMapper initObjectMapper(ObjectMapper objectMapper) {
        if (Objects.isNull(objectMapper)) {
            objectMapper = new ObjectMapper();
        }
        return doInitObjectMapper(objectMapper);
    }

    /**
     * 初始化 ObjectMapper 时间方法
     *
     * @param objectMapper
     * @return
     */
    @SuppressWarnings("deprecation")
    private static ObjectMapper doInitObjectMapper(ObjectMapper objectMapper) {
        // 开启全局漂亮的JSON字符串打印
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        // 禁用时间格式串行，使其输出正常的时间字符串格式
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 不显示为null的字段
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 忽略不能转移的字符
        objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
        // 过滤对象的null属性.
        // objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 忽略transient
        objectMapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
        return registerModule(objectMapper);
    }

    /**
     * 注册模块
     *
     * @param objectMapper
     * @return
     */
    private static ObjectMapper registerModule(ObjectMapper objectMapper) {
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        simpleModule.addDeserializer(LocalDateTime.class,
                new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        simpleModule.addDeserializer(LocalDate.class,
                new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        simpleModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        simpleModule.addDeserializer(LocalTime.class,
                new LocalTimeDeserializer((DateTimeFormatter.ofPattern("HH:mm:ss"))));
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }

    /**
     * 转换为 JSON 字符串
     *
     * @param obj
     * @return JSONString
     * @throws Exception
     */
    public static String obj2JSONString(Object obj) {
        if (obj == null) {
            return null;
        }
        String result = "";
        try {
            result = obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 转换为 JSON 字符串，并且将对象的大写转换为下划线加小写，例如：userName-->user_name
     *
     * @param object
     * @return JSONString
     * @throws Exception
     */
    public static String obj2UnderlineJSONString(Object object) {
        if (object == null) {
            return null;
        }
        String result = "";
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
            result = object instanceof String ? (String) object : mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 转换为 JSON 字符串，忽略空值
     *
     * @param obj
     * @return JSONString with ignore null
     * @throws Exception
     */

    public static String obj2JSONStringIgnoreNull(Object obj) {
        if (obj == null) {
            return null;
        }
        String result = "";
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            result = obj instanceof String ? (String) obj : mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 转换为 JSON 字符串，忽略空值, 并且将对象的大写转换为下划线加小写，例如：userName-->user_name
     *
     * @param object
     * @return JSONString
     * @throws Exception
     */
    public static String obj2UnderlineJSONStringIgnoreNull(Object object) {
        if (object == null) {
            return null;
        }
        String result = "";
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            result = object instanceof String ? (String) object : mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 转换为 JavaBean
     *
     * @param jsonString
     * @param typeReference
     * @return
     * @throws Exception
     */
    @SuppressWarnings({ "unchecked", "hiding" })
    public static <T> T json2Pojo(String jsonString, TypeReference<T> typeReference) {
        if (jsonString == null || jsonString.length() == 0 || typeReference == null) {
            return null;
        }
        try {
            objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
            return (T) (typeReference.getType().equals(String.class) ? jsonString
                    : objectMapper.readValue(jsonString, typeReference));
        } catch (JsonProcessingException e) {
            return null;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 转换为 JavaBean 并将下划线转换为驼峰的形式，例如：user_name-->userName
     *
     * @param json
     * @param typeReference
     * @return
     * @throws Exception
     */
    @SuppressWarnings({ "unchecked", "hiding" })
    public static <T> T json2toSnakePojo(String json, TypeReference<T> typeReference) {
        if (json == null || json.length() == 0 || typeReference == null) {
            return null;
        }
        try {
            ObjectMapper mapper = new ObjectMapper();
            // mapper的configure方法可以设置多种配置（例如：多字段 少字段的处理）
            // mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
            return (T) (typeReference.getType().equals(String.class) ? json : mapper.readValue(json, typeReference));
        } catch (JsonProcessingException e) {
            return null;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 字符串转换为 Map<String, Object>
     *
     * @param jsonString
     * @return
     * @throws Exception
     */
    @SuppressWarnings({ "unchecked", "hiding" })
    public static <T> Map<String, Object> json2map(String jsonString) {
        if (jsonString == null || jsonString.length() == 0) {
            return null;
        }
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            // 解决jackson2无法反序列化LocalDateTime的问题
            mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
            mapper.registerModule(new JavaTimeModule());
            return mapper.readValue(jsonString, Map.class);
        } catch (JsonProcessingException e) {
            return null;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 字符串转换为 Map<String, T>
     */
    @SuppressWarnings({ "unchecked", "hiding" })
    public static <T> Map<String, T> json2map(String jsonString, Class<T> clazz) {
        if (jsonString == null || jsonString.length() == 0 || clazz == null) {
            return null;
        }
        try {
            Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>) objectMapper.readValue(jsonString,
                    new TypeReference<Map<String, T>>() {
                    });
            Map<String, T> result = new HashMap<String, T>();
            for (Map.Entry<String, Map<String, Object>> entry : map.entrySet()) {
                result.put(entry.getKey(), map2pojo(entry.getValue(), clazz));
            }
            return result;
        } catch (JsonProcessingException e) {
            return null;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 深度转换 JSON 成 List
     *
     * @param json
     * @return
     */
    public static List<Object> json2listDeeply(String json) {
        if (json == null || json.length() == 0) {
            return null;
        }
        try {
            return json2ListRecursion(json, objectMapper);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 深度转换 JSON 成 Map
     *
     * @param json
     * @return
     */
    public static Map<String, Object> json2mapDeeply(String json) {
        if (json == null || json.length() == 0) {
            return null;
        }
        try {
            return json2MapRecursion(json, objectMapper);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 把 JSON 解析成 List，如果 List 内部的元素存在 jsonString，继续解析
     *
     * @param json
     * @param mapper 解析工具
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    private static List<Object> json2ListRecursion(String json, ObjectMapper mapper) throws Exception {
        if (json == null) {
            return null;
        }
        List<Object> list = mapper.readValue(json, List.class);

        for (Object obj : list) {
            if (obj != null && obj instanceof String) {
                String str = (String) obj;
                if (str.startsWith("[")) {
                    obj = json2ListRecursion(str, mapper);
                } else if (obj.toString().startsWith("{")) {
                    obj = json2MapRecursion(str, mapper);
                }
            }
        }

        return list;
    }

    /**
     * 把 JSON 解析成 Map，如果 Map 内部的 Value 存在 jsonString，继续解析
     *
     * @param json
     * @param mapper
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    private static Map<String, Object> json2MapRecursion(String json, ObjectMapper mapper) throws Exception {
        if (json == null) {
            return null;
        }

        Map<String, Object> map = mapper.readValue(json, Map.class);

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Object obj = entry.getValue();
            if (obj != null && obj instanceof String) {
                String str = ((String) obj);

                if (str.startsWith("[")) {
                    List<?> list = json2ListRecursion(str, mapper);
                    map.put(entry.getKey(), list);
                } else if (str.startsWith("{")) {
                    Map<String, Object> mapRecursion = json2MapRecursion(str, mapper);
                    map.put(entry.getKey(), mapRecursion);
                }
            }
        }

        return map;
    }

    /**
     * 将 JSON 数组转换为集合
     *
     * @param jsonArrayStr
     * @param clazz
     * @return
     * @throws Exception
     */
    @SuppressWarnings({ "unchecked", "hiding" })
    public static <T> List<T> json2list(String jsonArrayStr, Class<T> clazz) {
        if (jsonArrayStr == null || jsonArrayStr.length() == 0 || clazz == null) {
            return null;
        }
        JavaType javaType = getCollectionType(ArrayList.class, clazz);
        List<T> list = null;
        try {
            list = (List<T>) objectMapper.readValue(jsonArrayStr, javaType);
        } catch (JsonProcessingException e) {
            return null;
        } catch (IOException e) {
            return null;
        }
        return list;
    }

    /**
     * 获取泛型的 Collection Type
     *
     * @param collectionClass 泛型的Collection
     * @param elementClasses  元素类
     * @return JavaType Java类型
     * @since 1.0
     */
    public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        if (collectionClass == null || elementClasses == null) {
            return null;
        }
        return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }

    /**
     * 将 Map 转换为 JavaBean
     *
     * @param map
     * @param clazz
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static <T> T map2pojo(Map map, Class<T> clazz) {
        if (map.size() == 0 || clazz == null) {
            return null;
        }
        return objectMapper.convertValue(map, clazz);
    }

    /**
     * 将 Map 转换为 JSON
     *
     * @param map
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static String mapToJson(Map map) {
        if (map.size() == 0) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(map);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 将 JSON 对象转换为 JavaBean
     *
     * @param obj
     * @param clazz
     * @return
     */
    public static <T> T obj2pojo(Object obj, Class<T> clazz) {
        if (obj == null || clazz == null) {
            return null;
        }
        // 解决jackson2无法反序列化LocalDateTime的问题
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());

        return objectMapper.convertValue(obj, clazz);
    }

    /**
     * 将 obj 对象转换为 Map<String, Object>
     *
     * @param obj
     * @return
     */
    public static Map<String, Object> obj2map(Object obj) {
        // 解决jackson2无法反序列化LocalDateTime的问题
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new JavaTimeModule());
        if (obj == null) {
            return new HashMap<String, Object>();
        }
        return JacksonUtils.json2map(JacksonUtils.obj2JSONString(obj));
    }

    /**
     * 将 JSON 对象转换为 JavaBean
     *
     * @param obj
     * @param clazz
     * @return
     */
    public static <T> T obj2pojoIgnoreNull(Object obj, Class<T> clazz) {
        if (obj == null || clazz == null) {
            return null;
        }
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 解决jackson2无法反序列化LocalDateTime的问题
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.registerModule(new JavaTimeModule());

        return mapper.convertValue(obj, clazz);
    }

    /**
     * 将 对象集合 转化为对应的 javaBean
     *
     * @param objList
     * @param entityVOType
     * @return
     */
    public static <T, M> List<M> listObj2pojo(List<T> objList, Class<M> entityVOType) {
        if (objList == null || entityVOType == null) {
            return null;
        }
        List<M> newRecordList = new ArrayList<>();
        for (T record : objList) {
            M newRecord = JacksonUtils.obj2pojo(record, entityVOType);
            newRecordList.add(newRecord);
        }
        return newRecordList;
    }

    /**
     * 将 对象集合 转化为对应的 javaBean
     *
     * @param objList
     * @param entityVOType
     * @return
     */
    public static <T, M> List<M> listObj2pojoIgnoreNull(List<T> objList, Class<M> entityVOType) {
        if (objList == null || objList.size() == 0 || entityVOType == null) {
            return null;
        }
        List<M> newRecordList = new ArrayList<>();
        for (T record : objList) {
            M newRecord = JacksonUtils.obj2pojoIgnoreNull(record, entityVOType);
            newRecordList.add(newRecord);
        }
        return newRecordList;
    }

    /**
     * 将 对象集合 转化为对应的 map 集合
     *
     * @param objList
     * @param objList
     * @return
     */
    public static <T> List<Map<String, Object>> listObj2map(List<T> objList) {
        if (objList == null || objList.size() == 0) {
            return null;
        }
        List<Map<String, Object>> newRecordList = new ArrayList<>();
        for (T record : objList) {
            Map<String, Object> newRecord = JacksonUtils.json2map(JacksonUtils.obj2JSONString(record));
            newRecordList.add(newRecord);
        }
        return newRecordList;
    }

}
